/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package treeship.core.ship.impl;

import java.util.List;
import java.util.Map;

import treeship.core.archetype.ArchetypeProperty;
import treeship.core.archetype.ArchetypeShip;
import treeship.core.exceptions.CantDoThatException;
import treeship.core.ship.TreeData;
import treeship.core.ship.TreeOperations;
import treeship.core.ship.space.Position;
import treeship.core.ship.space.Shape;
import treeship.core.ship.space.Speed;

/**
 * 
 * @author Moonchild
 */
public abstract class AbstractTreeShip<T extends AbstractTreeShip> implements
		TreeData<T>, TreeOperations<T> {

	@Override
	public void clientAttach() throws CantDoThatException {
		checkParentExist();
		getParent().serverAcceptChild(this);
	}

	@Override
	public void clientDetach() throws CantDoThatException {
		checkParentExist();
		getParent().serverRemoveChild(this);
	}

	@Override
	public abstract String getSelfDescription();

	@Override
	public abstract Map<ArchetypeProperty, Double> getProperties();

	@Override
	public abstract void clientClimb() throws CantDoThatException;

	@Override
	public abstract void clientDescend(T brother) throws CantDoThatException;

	@Override
	public abstract List<T> getInners();

	@Override
	public abstract T getParent();

	@Override
	public abstract List<T> getChildren();

	@Override
	public abstract Shape getShape();

	@Override
	public abstract ArchetypeShip getArchetype();

	@Override
	public abstract void spawnInner(ArchetypeShip archetype)
			throws CantDoThatException;

	protected abstract void serverAcceptChild(T child)
			throws CantDoThatException;

	protected abstract void serverRemoveChild(T child)
			throws CantDoThatException;

	protected abstract void serverExit(T child) throws CantDoThatException;

	protected abstract void serverEnter(T child) throws CantDoThatException;

	protected void checkIsBrother(TreeShip s) throws CantDoThatException {
		checkParentExist();
		getParent().checkIsInner(s);
		if (!getParent().getInners().contains(s)) {
			CantDoThatException.cantDo("L\'elemento non � un mio brother");
		}
	}

	protected void checkIsChild(TreeShip s) throws CantDoThatException {
		if (!getChildren().contains(s)) {
			CantDoThatException.cantDo("L\'elemento non � mio child");
		}
	}

	protected void checkIsInner(TreeShip s) throws CantDoThatException {
		if (!getInners().contains(s)) {
			CantDoThatException.cantDo("L\'elemento non � al mio interno");
		}
	}

	protected void checkParentExist() throws CantDoThatException {
		if (getParent() == null) {
			CantDoThatException.cantDo("Parent null");
		}
	}

	@Override
	public abstract Double getOtherProperty(String s);

	@Override
	public boolean isTouching(TreeData destination) {
		if (!(this.getParent() != null && destination.getParent() != null && this
				.getParent() == destination.getParent())) {
			return false;
		}
		if (firstPositionInSecond(destination, this)) {
			return true;
		}
		if (firstPositionInSecond(this, destination)) {
			return true;
		}
		return false;
	}

	@Override
	public boolean isWellInsideTheSpaceOf(TreeData other) {
		if (!(this.getParent() != null && other.getParent() != null && this
				.getParent() == other.getParent())) {
			return false;
		}
		double x1 = this.getPosition().getX();
		double x2 = this.getPosition().getX()
				+ this.getShape().getDimension().getValue();

		double x3 = other.getPosition().getX();
		double x4 = other.getPosition().getX()
				+ other.getShape().getDimension().getValue();
		if (x3 <= x1 && x1 <= x4) {
			if (x3 <= x2 && x2 <= x4) {
				return true;
			}
		}
		return false;

	}

	private static boolean firstPositionInSecond(TreeData destination,
			TreeData source) {
		if (source.getPosition().getX() <= destination.getPosition().getX()) {
			if (destination.getPosition().getX() <= source.getPosition().getX()
					+ source.getShape().getDimension().getValue()) {
				return true;
			}
		}
		return false;
	}

	@Override
	public abstract void goTo(Position p) throws CantDoThatException;

	@Override
	public abstract Speed getSpeed();
}
